﻿namespace wwm.LeetCodeHelper.Servers;
public class Leetcode
{

    public static string leetcode_cookie_session_key { get; set; } = "LEETCODE_SESSION";
    public static string leetcode_cookie_csrftoken_key { get; set; } = "csrftoken";
    string session { get; set; }
    string csrfToken { get; set; }
    static Uris uris { get; set; }

    static int retry_num = 0;
    public static void setUris(Uris uris)
    {
        Leetcode.uris = uris;
    }
    #region build
    private Credit credit()
    {
        return new Credit
        {
            session = this.session,
            csrfToken = this.csrfToken
        };
    }

    public static async Task<Leetcode> BuildAsync(string username, string password, EndPoint endpoint)
    {
        Helper.switchEndPoint(endpoint);
        //var credit = await login(username, password);
        var cookies = await Helper.GetCookie(username, password);
        Credit credit = SetCredit(cookies);
        Helper.setCredit(credit);
        Helper.setHttpClient(new HttpClient());
        return new Leetcode();
    }

    public static async Task<Leetcode> BuildAsync(string cookie, EndPoint endpoint)
    {
        Helper.switchEndPoint(endpoint);
        Credit credit = SetCredit(new Dictionary<string, string>() { { leetcode_cookie_session_key, cookie } });
        Helper.setCredit(credit);
        Helper.setHttpClient(new HttpClient());
        await Task.CompletedTask;
        return new Leetcode();
    }

    public static async Task<Credit> LoginAsync(string username, string password)
    {
        // got login token first
        var response = await Helper.HttpRequest(new HttpRequestOptions()
        {
            url = Leetcode.uris.Login,
            resolveWithFullResponse = true,
        });
        IEnumerable<string> setCookies = null;
        if (!response.Headers.TryGetValues("set-cookie", out setCookies))
        {
            throw new Exception("获取cookies失败了");
        }
        var token = Helper.parseCookie(setCookies.ToArray(), "csrftoken");
        // Leetcode CN return null here, but it's does not matter
        Credit credit = new Credit
        {
            csrfToken = token,
        };
        Helper.setCredit(credit);

        //then login
        try
        {
            var _response = await Helper.HttpRequest(new HttpRequestOptions()
            {
                method = "post",
                url = Leetcode.uris.Login,
                form = new Dictionary<string, string>()
                {
                    { "csrfmiddlewaretoken", "token" },
                    { "login","username" },
                    { "password","password" },
                },
                resolveWithFullResponse = true,
            });
            if (!_response.Headers.TryGetValues("set-cookie", out setCookies))
            {
                throw new Exception("获取cookies失败了");
            }
            var session = Helper.parseCookie(setCookies.ToArray(), "LEETCODE_SESSION");
            var csrfToken = Helper.parseCookie(setCookies.ToArray(), "csrftoken");
            //credit = new Credit
            //{
            //    session = session,
            //    csrfToken = csrfToken
            //};
            Dictionary<string, string> cookies = new Dictionary<string, string>();
            cookies.Add("LEETCODE_SESSION", session);
            cookies.Add("csrftoken", csrfToken);
            credit = SetCredit(cookies);
        }
        catch (Exception e)
        {
            throw new Exception("登录失败了,详细信息:" + e.Message);
        }
        return credit;
    }

    public static Credit SetCredit(Dictionary<string, string> cookies)
    {
        Credit credit = new Credit()
        {
            //可以只使用session
            session = cookies[leetcode_cookie_session_key],
            //csrfToken = cookies[leetcode_cookie_csrftoken_key]
        };
        return credit;
    }
    #endregion
    #region connection leetcode api
    /// <summary>
    /// 获取每日一题的完成情况
    /// </summary>
    /// <returns></returns>
    public static async Task<TodayRecordDto> queryTodayRecordAsync()
    {
        var response = await Helper.GraphQLRequest(new GraphQLRequestOptions()
        {
            operationName = "todayRecord",
            query = "query todayRecord {\n  todayRecord {\n    userStatus\n    comboTimes\n    __typename\n  }\n}\n",
            variables = null,
        });
        if (string.IsNullOrWhiteSpace(response))
        {
            return null;
        }
        var data = JsonSerializer.Deserialize<TodayRecordDto>(response);
        return data;
    }

    /// <summary>
    /// 获取每日一题titleSlug
    /// </summary>
    /// <returns></returns>
    public static async Task<string> GetQuestionOfTodaTitleSlugyAsync()
    {
        var response = await Helper.GraphQLRequest(new GraphQLRequestOptions()
        {
            operationName = "questionOfToday",
            query = "query questionOfToday {\n  todayRecord {\n    question {\n      titleSlug\n      __typename\n    }\n    __typename\n  }\n}\n",
            variables = null
        });
        var data = JsonSerializer.Deserialize<TodayTitleSlugDto>(response);
        string titleSlug = data?.data?.todayRecord?[0]?.question.titleSlug;
        return titleSlug;
    }
    /// <summary>
    /// 根据titleSlug获取题目信息
    /// </summary>
    /// <param name="titleSlug"></param>
    /// <returns></returns>
    public static async Task<string> GetQuestionFromUrlAsync(string titleSlug)
    {
        var response = await Helper.GraphQLRequest(new GraphQLRequestOptions()
        {
            operationName = "questionData",
            query = "query questionData($titleSlug: String!) {\n  question(titleSlug: $titleSlug) {\n    questionId\n    questionFrontendId\n    categoryTitle\n    boundTopicId\n    title\n    titleSlug\n    content\n    translatedTitle\n    translatedContent\n    isPaidOnly\n    difficulty\n    likes\n    dislikes\n    isLiked\n    similarQuestions\n    contributors {\n      username\n      profileUrl\n      avatarUrl\n      __typename\n    }\n    langToValidPlayground\n    topicTags {\n      name\n      slug\n      translatedName\n      __typename\n    }\n    companyTagStats\n    codeSnippets {\n      lang\n      langSlug\n      code\n      __typename\n    }\n    stats\n    hints\n    solution {\n      id\n      canSeeDetail\n      __typename\n    }\n    status\n    sampleTestCase\n    metaData\n    judgerAvailable\n    judgeType\n    mysqlSchemas\n    enableRunCode\n    envInfo\n    book {\n      id\n      bookName\n      pressName\n      source\n      shortDescription\n      fullDescription\n      bookImgUrl\n      pressImgUrl\n      productUrl\n      __typename\n    }\n    isSubscribed\n    isDailyQuestion\n    dailyRecordStatus\n    editorType\n    ugcQuestionId\n    style\n    exampleTestcases\n    __typename\n  }\n}\n",
            variables = new variablesDto()
            {
                titleSlug = titleSlug
            }
        });
        return response;
    }
    /// <summary>
    /// 获取用户信息
    /// </summary>
    /// <returns></returns>
    public static async Task<UserProfileRecord> GetProfileAsync()
    {
        var response = await Helper.GraphQLRequest(new GraphQLRequestOptions()
        {
            query = @"
            {
                user {
                    username
                }
            }
            "
        });
        return JsonSerializer.Deserialize<UserProfileRecord>(response);
    }

    /// <summary>
    /// 获取所有问题
    /// </summary>
    /// <returns></returns>
    public static async Task<AllProblemSimpleDto> GetAllProblemsAsync()
    {
        var response = await Helper.GraphQLRequest(new GraphQLRequestOptions()
        {
            operationName = "allQuestions",
            query = "query allQuestions {\n  allQuestionsBeta {\n    ...questionSummaryFields\n    __typename\n  }\n}\n\nfragment questionSummaryFields on QuestionNode {\n  title\n  titleSlug\n  translatedTitle\n  questionId\n  questionFrontendId\n  status\n  difficulty\n  isPaidOnly\n  categoryTitle\n  __typename\n}\n"
        });
        if (string.IsNullOrWhiteSpace(response))
        {
            return null;
        }
        var data = JsonSerializer.Deserialize<AllProblemSimpleDto>(response);
        return data;
    }
    /// <summary>
    /// 根据标签获取problems
    /// </summary>
    /// <param name="tag"></param>
    /// <returns></returns>
    public static async Task<AllProblemByTagDto> GetProblemsByTagAsync(string tag)
    {
        var response = await Helper.GraphQLRequest(new GraphQLRequestOptions()
        {
            query = @"
                query getTopicTag($slug: String!) {
                    topicTag(slug: $slug) {
                        questions {
                            status
                            questionId
                            title
                            titleSlug
                            stats
                            difficulty
                            isPaidOnly
                            topicTags {
                                slug
                            }
                        }
                    }
                }
            ",
            variables = new variablesDto()
            {
                slug = tag
            }
        });
        var data = JsonSerializer.Deserialize<AllProblemByTagDto>(response);
        return data;
    }


    public static async Task<SearchProblemDto> GetProblemsByQuestionId(int questionId, int skip = 0)
    {
        if (skip == 0) retry_num = 0;
        if (retry_num >= 50) return null;
        retry_num++;
        System.Console.WriteLine(retry_num);
        var response = await Helper.GraphQLRequest(new GraphQLRequestOptions()
        {
            query = @"
                  query problemsetQuestionList($categorySlug: String, $limit: Int, $skip: Int, $filters: QuestionListFilterInput) {
                  problemsetQuestionList(
                    categorySlug: $categorySlug
                    limit: $limit
                    skip: $skip
                    filters: $filters
                  ) {
                    questions {
                      frontendQuestionId
                      paidOnly
                      solutionNum
                      title
                      titleCn
                      titleSlug
                    }
                  }
                }
            ",
            variables = new variablesDto()
            {
                categorySlug = "",
                limit = 10,
                skip = skip,
                filters = new Dictionary<string, string>()
                {
                    { "searchKeywords",questionId+"" }
                }
            }
        });
        var data = JsonSerializer.Deserialize<SearchProblemDto>(response);
        if (data?.data?.problemsetQuestionList.questions == null) return null;
        var item = data?.data?.problemsetQuestionList.questions?.FirstOrDefault(a => a.frontendQuestionId == questionId.ToString());
        if (item != null)
        {
            return data;
        }
        else
        {
            skip += 10;
            return await GetProblemsByQuestionId(questionId, skip);
        }
    }

    /// <summary>
    /// 提交答案
    /// </summary>
    /// <param name="record"></param>
    /// <returns></returns>
    public static async Task<object> Sumit(SubmitRecord record)
    {
        var options = new HttpRequestOptions();
        options.method = HttpMethod.Post.Method;
        options.body = JsonSerializer.Serialize(record);
        var response = await Helper.HttpRequest(options);
        Console.WriteLine(response);
        return response;
    }
    #endregion

    #region Dtos
    public record UserProfileRecord
    {
        public UserRecord data { get; set; }
    }
    public record UserRecord
    {
        public UserNameRecord user { get; set; }
    }
    public record UserNameRecord
    {
        public string username { get; set; }
    }
    /// <summary>
    /// 提交body
    /// </summary>
    public record SubmitRecord
    {
        public string question_id { get; set; }
        public string lang { get; set; } = "csharp";
        public string typed_code { get; set; }
        public bool test_mode { get; set; } = false;
        public string test_judger { get; set; } = string.Empty;
        public string questionSlug { get; set; }
    }

    /// <summary>
    /// 提交返回结果
    /// </summary>
    public record SubmitResponseRecord
    {
        /// <summary>
        /// 错误
        /// </summary>
        public string error { get; set; }
        /// <summary>
        /// 提交id
        /// </summary>
        public string submission_id { get; set; }
    }
    #endregion
}
